import {
    formatDataToArray,
    walk,
    getTopAncestorsFomNodeList,
    getNodeListBoundingRect,
    createUid,
} from '../utils';

// 解析要添加外框的节点实例列表
const parseAddNodeList = (list) => {
    // 找出顶层节点
    list = getTopAncestorsFomNodeList(list);
    const cache = {};
    const uidToParent = {};
    // 找出列表中节点在兄弟节点中的索引，并和父节点关联起来
    list.forEach((node) => {
        const parent = node.parent;
        if (parent) {
            const pUid = parent.uid;
            uidToParent[pUid] = parent;
            const index = node.getIndexInBrothers();
            const data = {
                node,
                index,
            };
            if (cache[pUid]) {
                if (
                    !cache[pUid].find((item) => {
                        return item.index === data.index;
                    })
                ) {
                    cache[pUid].push(data);
                }
            } else {
                cache[pUid] = [data];
            }
        }
    });
    const res = [];
    Object.keys(cache).forEach((uid) => {
        const indexList = cache[uid];
        const parentNode = uidToParent[uid];
        if (indexList.length > 1) {
            // 多个节点
            const rangeList = indexList
                .map((item) => {
                    return item.index;
                })
                .sort((a, b) => {
                    return a - b;
                });
            const minIndex = rangeList[0];
            const maxIndex = rangeList[rangeList.length - 1];
            let curStart = -1;
            let curEnd = -1;
            for (let i = minIndex; i <= maxIndex; i++) {
                // 连续索引
                if (rangeList.includes(i)) {
                    if (curStart === -1) {
                        curStart = i;
                    }
                    curEnd = i;
                } else {
                    // 连续断开
                    if (curStart !== -1 && curEnd !== -1) {
                        res.push({
                            node: parentNode,
                            range: [curStart, curEnd],
                        });
                    }
                    curStart = -1;
                    curEnd = -1;
                }
            }
            // 不要忘了最后一段索引
            if (curStart !== -1 && curEnd !== -1) {
                res.push({
                    node: parentNode,
                    range: [curStart, curEnd],
                });
            }
        } else {
            // 单个节点
            res.push({
                node: parentNode,
                range: [indexList[0].index, indexList[0].index],
            });
        }
    });
    return res;
};

// 解析获取节点的子节点生成的外框列表
const getNodeOuterFrameList = (node) => {
    const children = node.children;
    if (!children || children.length <= 0) return;
    const res = [];
    const map = {};
    children.forEach((item, index) => {
        const outerFrameData = item.getData('outerFrame');
        if (!outerFrameData) return;
        const groupId = outerFrameData.groupId;
        if (groupId) {
            if (!map[groupId]) {
                map[groupId] = [];
            }
            map[groupId].push({
                node: item,
                index,
            });
        } else {
            res.push({
                nodeList: [item],
                range: [index, index],
            });
        }
    });
    Object.keys(map).forEach((id) => {
        const list = map[id];
        res.push({
            nodeList: list.map((item) => {
                return item.node;
            }),
            range: [list[0].index, list[list.length - 1].index],
        });
    });
    return res;
};

// 默认外框样式
const defaultStyle = {
    radius: 5,
    strokeWidth: 2,
    strokeColor: '#0984e3',
    strokeDasharray: '5,5',
    fill: 'rgba(9,132,227,0.05)',
};

// 外框插件
class OuterFrame {
    constructor(opt = {}) {
        this.mindMap = opt.mindMap;
        this.draw = null;
        this.createDrawContainer();
        this.outerFrameElList = [];
        this.activeOuterFrame = null;
        this.bindEvent();
    }

    // 创建容器
    createDrawContainer() {
        this.draw = this.mindMap.draw.group();
        this.draw.addClass('smm-outer-frame-container');
        this.draw.back(); // 最底层
        this.draw.forward(); // 连线层上面
    }

    // 绑定事件
    bindEvent() {
        this.renderOuterFrames = this.renderOuterFrames.bind(this);
        this.mindMap.on('node_tree_render_end', this.renderOuterFrames);
        this.mindMap.on('data_change', this.renderOuterFrames);
        // 监听画布和节点点击事件，用于清除当前激活的连接线
        this.clearActiveOuterFrame = this.clearActiveOuterFrame.bind(this);
        this.mindMap.on('draw_click', this.clearActiveOuterFrame);
        this.mindMap.on('node_click', this.clearActiveOuterFrame);

        this.addOuterFrame = this.addOuterFrame.bind(this);
        this.mindMap.command.add('ADD_OUTER_FRAME', this.addOuterFrame);

        this.removeActiveOuterFrame = this.removeActiveOuterFrame.bind(this);
        this.mindMap.keyCommand.addShortcut(
            'Del|Backspace',
            this.removeActiveOuterFrame,
        );
    }

    // 解绑事件
    unBindEvent() {
        this.mindMap.off('node_tree_render_end', this.renderOuterFrames);
        this.mindMap.off('data_change', this.renderOuterFrames);
        this.mindMap.off('draw_click', this.clearActiveOuterFrame);
        this.mindMap.off('node_click', this.clearActiveOuterFrame);
        this.mindMap.command.remove('ADD_OUTER_FRAME', this.addOuterFrame);
        this.mindMap.keyCommand.removeShortcut(
            'Del|Backspace',
            this.removeActiveOuterFrame,
        );
    }

    // 给节点添加外框数据
    /*
  config: {
    text: '',
    radius: 5,
    strokeWidth: 2,
    strokeColor: '#0984e3',
    strokeDasharray: '5,5',
    fill: 'rgba(9,132,227,0.05)'
  }
  */
    addOuterFrame(appointNodes, config = {}) {
        appointNodes = formatDataToArray(appointNodes);
        const activeNodeList = this.mindMap.renderer.activeNodeList;
        if (activeNodeList.length <= 0 && appointNodes.length <= 0) {
            return;
        }
        let nodeList = appointNodes.length > 0 ? appointNodes : activeNodeList;
        nodeList = nodeList.filter((node) => {
            return !node.isRoot && !node.isGeneralization;
        });
        const list = parseAddNodeList(nodeList);
        list.forEach(({ node, range }) => {
            const childNodeList = node.children.slice(range[0], range[1] + 1);
            const groupId = createUid();
            childNodeList.forEach((child) => {
                let outerFrame = child.getData('outerFrame');
                // 检查该外框是否已存在
                if (outerFrame) {
                    outerFrame = {
                        ...outerFrame,
                        ...config,
                        groupId,
                    };
                } else {
                    outerFrame = {
                        ...config,
                        groupId,
                    };
                }
                this.mindMap.execCommand('SET_NODE_DATA', child, {
                    outerFrame,
                });
            });
        });
    }

    // 获取当前激活的外框
    getActiveOuterFrame() {
        return this.activeOuterFrame
            ? {
                  ...this.activeOuterFrame,
              }
            : null;
    }

    // 删除当前激活的外框
    removeActiveOuterFrame() {
        if (!this.activeOuterFrame) return;
        const { node, range } = this.activeOuterFrame;
        this.getRangeNodeList(node, range).forEach((child) => {
            this.mindMap.execCommand('SET_NODE_DATA', child, {
                outerFrame: null,
            });
        });
        this.mindMap.emit('outer_frame_delete');
    }

    // 更新当前激活的外框
    // 执行了该方法后请立即隐藏你的样式面板，因为会清除当前激活的外框
    updateActiveOuterFrame(config = {}) {
        if (!this.activeOuterFrame) return;
        const { node, range } = this.activeOuterFrame;
        this.getRangeNodeList(node, range).forEach((node) => {
            const outerFrame = node.getData('outerFrame');
            this.mindMap.execCommand('SET_NODE_DATA', node, {
                outerFrame: {
                    ...outerFrame,
                    ...config,
                },
            });
        });
    }

    // 获取某个节点指定范围的带外框的子节点列表
    getRangeNodeList(node, range) {
        return node.children.slice(range[0], range[1] + 1).filter((child) => {
            return child.getData('outerFrame');
        });
    }

    // 渲染外框
    renderOuterFrames() {
        this.clearOuterFrameElList();
        let tree = this.mindMap.renderer.root;
        if (!tree) return;
        const t = this.mindMap.draw.transform();
        const { outerFramePaddingX, outerFramePaddingY } = this.mindMap.opt;
        walk(
            tree,
            null,
            (cur) => {
                if (!cur) return;
                const outerFrameList = getNodeOuterFrameList(cur);
                if (outerFrameList && outerFrameList.length > 0) {
                    outerFrameList.forEach(({ nodeList, range }) => {
                        if (range[0] === -1 || range[1] === -1) return;
                        const { left, top, width, height } =
                            getNodeListBoundingRect(nodeList);
                        if (
                            !Number.isFinite(left) ||
                            !Number.isFinite(top) ||
                            !Number.isFinite(width) ||
                            !Number.isFinite(height)
                        )
                            return;
                        const el = this.createOuterFrameEl(
                            (left -
                                outerFramePaddingX -
                                this.mindMap.elRect.left -
                                t.translateX) /
                                t.scaleX,
                            (top -
                                outerFramePaddingY -
                                this.mindMap.elRect.top -
                                t.translateY) /
                                t.scaleY,
                            (width + outerFramePaddingX * 2) / t.scaleX,
                            (height + outerFramePaddingY * 2) / t.scaleY,
                            nodeList[0].getData('outerFrame'), // 使用第一个节点的外框样式
                        );
                        el.on('click', (e) => {
                            e.stopPropagation();
                            this.setActiveOuterFrame(el, cur, range);
                        });
                    });
                }
            },
            () => {},
            true,
            0,
        );
    }

    // 激活外框
    setActiveOuterFrame(el, node, range) {
        this.mindMap.execCommand('CLEAR_ACTIVE_NODE');
        this.clearActiveOuterFrame();
        this.activeOuterFrame = {
            el,
            node,
            range,
        };
        el.stroke({
            dasharray: 'none',
        });
        this.mindMap.emit('outer_frame_active', el, node, range);
    }

    // 清除当前激活的外框
    clearActiveOuterFrame() {
        if (!this.activeOuterFrame) return;
        const { el } = this.activeOuterFrame;
        el.stroke({
            dasharray: el.cacheStyle.dasharray || defaultStyle.strokeDasharray,
        });
        this.activeOuterFrame = null;
    }

    // 创建外框元素
    createOuterFrameEl(x, y, width, height, styleConfig = {}) {
        styleConfig = { ...defaultStyle, ...styleConfig };
        const el = this.draw
            .rect()
            .size(width, height)
            .radius(styleConfig.radius)
            .stroke({
                width: styleConfig.strokeWidth,
                color: styleConfig.strokeColor,
                dasharray: styleConfig.strokeDasharray,
            })
            .fill({
                color: styleConfig.fill,
            })
            .x(x)
            .y(y);
        el.cacheStyle = {
            dasharray: styleConfig.strokeDasharray,
        };
        this.outerFrameElList.push(el);
        return el;
    }

    // 清除外框元素
    clearOuterFrameElList() {
        this.outerFrameElList.forEach((item) => {
            item.remove();
        });
        this.outerFrameElList = [];
        this.activeOuterFrame = null;
    }

    // 插件被移除前做的事情
    beforePluginRemove() {
        this.unBindEvent();
    }

    // 插件被卸载前做的事情
    beforePluginDestroy() {
        this.unBindEvent();
    }
}

OuterFrame.instanceName = 'outerFrame';

export default OuterFrame;
